home *** CD-ROM | disk | FTP | other *** search
/ PC Advisor 2011 May / PC Advisor 190 E.iso / pc / ESSENTIALS / VLC Media Player 1.1 / vlc-1.1.5-win32.exe / lua / intf / rc.lua < prev    next >
Encoding:
Text File  |  2010-11-13  |  27.9 KB  |  752 lines

  1. --[==========================================================================[
  2.  rc.lua: remote control module for VLC
  3. --[==========================================================================[
  4.  Copyright (C) 2007-2009 the VideoLAN team
  5.  $Id$
  6.  
  7.  Authors: Antoine Cellerier <dionoea at videolan dot org>
  8.  
  9.  This program is free software; you can redistribute it and/or modify
  10.  it under the terms of the GNU General Public License as published by
  11.  the Free Software Foundation; either version 2 of the License, or
  12.  (at your option) any later version.
  13.  
  14.  This program is distributed in the hope that it will be useful,
  15.  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  GNU General Public License for more details.
  18.  
  19.  You should have received a copy of the GNU General Public License
  20.  along with this program; if not, write to the Free Software
  21.  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22. --]==========================================================================]
  23.  
  24. description=
  25. [============================================================================[
  26.  Remote control interface for VLC
  27.  
  28.  This is a modules/control/rc.c look alike (with a bunch of new features)
  29.  
  30.  Use on local term:
  31.     vlc -I rc
  32.  Use on tcp connection:
  33.     vlc -I rc --lua-config "rc={host='localhost:4212'}"
  34.  Use on multiple hosts (term + 2 tcp ports):
  35.     vlc -I rc --lua-config "rc={hosts={'*console','localhost:4212','localhost:5678'}}"
  36.  
  37.  Note:
  38.     -I rc and -I luarc are aliases for -I lua --lua-intf rc
  39.  
  40.  Configuration options setable throught the --lua-config option are:
  41.     * hosts: A list of hosts to listen on.
  42.     * host: A host to listen on. (won't be used if `hosts' is set)
  43.     * eval: Add eval command to evaluate lua expressions. Set to any value to
  44.             enable.
  45.  The following can be set using the --lua-config option or in the interface
  46.  itself using the `set' command:
  47.     * prompt: The prompt.
  48.     * welcome: The welcome message.
  49.     * width: The default terminal width (used to format text).
  50.     * autocompletion: When issuing an unknown command, print a list of
  51.                       possible commands to autocomplete with. (0 to disable,
  52.                       1 to enable).
  53.     * autoalias: If autocompletion returns only one possibility, use it
  54.                  (0 to disable, 1 to enable).
  55.     * flatplaylist: 0 to disable, 1 to enable.
  56. ]============================================================================]
  57.  
  58. require("common")
  59. skip = common.skip
  60. skip2 = function(foo) return skip(skip(foo)) end
  61. setarg = common.setarg
  62. strip = common.strip
  63.  
  64. _ = vlc.gettext._
  65. N_ = vlc.gettext.N_
  66.  
  67. --[[ Setup default environement ]]
  68. env = { prompt = "> ";
  69.         width = 70;
  70.         autocompletion = 1;
  71.         autoalias = 1;
  72.         welcome = _("Remote control interface initialized. Type `help' for help.");
  73.         flatplaylist = 0;
  74.       }
  75.  
  76. --[[ Import custom environement variables from the command line config (if possible) ]]
  77. for k,v in pairs(env) do
  78.     if config[k] then
  79.         if type(env[k]) == type(config[k]) then
  80.             env[k] = config[k]
  81.             vlc.msg.dbg("set environement variable `"..k.."' to "..tostring(env[k]))
  82.         else
  83.             vlc.msg.err("environement variable `"..k.."' should be of type "..type(env[k])..". config value will be discarded.")
  84.         end
  85.     end
  86. end
  87.  
  88. --[[ Command functions ]]
  89. function set_env(name,client,value)
  90.     if value then
  91.         local var,val = split_input(value)
  92.         if val then
  93.             local s = string.gsub(val,"\"(.*)\"","%1")
  94.             if type(client.env[var])==type(1) then
  95.                 client.env[var] = tonumber(s)
  96.             else
  97.                 client.env[var] = s
  98.             end
  99.         else
  100.             client:append( tostring(client.env[var]) )
  101.         end
  102.     else
  103.         for e,v in common.pairs_sorted(client.env) do
  104.             client:append(e.."="..v)
  105.         end
  106.     end
  107. end
  108.  
  109. function save_env(name,client,value)
  110.     env = common.table_copy(client.env)
  111. end
  112.  
  113. function alias(client,value)
  114.     if value then
  115.         local var,val = split_input(value)
  116.         if commands[var] and type(commands[var]) ~= type("") then
  117.             client:append("Error: cannot use a primary command as an alias name")
  118.         else
  119.             if commands[val] then
  120.                 commands[var]=val
  121.             else
  122.                 client:append("Error: unknown primary command `"..val.."'.")
  123.             end
  124.         end
  125.     else
  126.         for c,v in common.pairs_sorted(commands) do
  127.             if type(v)==type("") then
  128.                 client:append(c.."="..v)
  129.             end
  130.         end
  131.     end
  132. end
  133.  
  134. function fixme(name,client)
  135.     client:append( "FIXME: unimplemented command `"..name.."'." )
  136. end
  137.  
  138. function logout(name,client)
  139.     if client.type == host.client_type.net then
  140.         client:send("Bye-bye!")
  141.         client:del()
  142.     else
  143.         client:append("Error: Can't logout of stdin/stdout. Use quit or shutdown to close VLC.")
  144.     end
  145. end
  146.  
  147. function shutdown(name,client)
  148.     client:append("Bye-bye!")
  149.     h:broadcast("Shutting down.")
  150.     vlc.msg.info("Requested shutdown.")
  151.     vlc.misc.quit()
  152. end
  153.  
  154. function quit(name,client)
  155.     if client.type == host.client_type.net then
  156.         logout(name,client)
  157.     else
  158.         shutdown(name,client)
  159.     end
  160. end
  161.  
  162. function add(name,client,arg)
  163.     -- TODO: parse single and double quotes properly
  164.     local f
  165.     if name == "enqueue" then
  166.         f = vlc.playlist.enqueue
  167.     else
  168.         f = vlc.playlist.add
  169.     end
  170.     local options = {}
  171.     for o in string.gmatch(arg," +:([^ ]*)") do
  172.         table.insert(options,o)
  173.     end
  174.     arg = string.gsub(arg," +:.*$","")
  175.     f({{path=arg,options=options}})
  176. end
  177.  
  178. function playlist_is_tree( client )
  179.     if client.env.flatplaylist == 0 then
  180.         return true
  181.     else
  182.         return false
  183.     end
  184. end
  185.  
  186. function playlist(name,client,arg)
  187.     function playlist0(item,prefix)
  188.         local prefix = prefix or ""
  189.         if not item.flags.disabled then
  190.             local str = "| "..prefix..tostring(item.id).." - "..item.name
  191.             if item.duration > 0 then
  192.                 str = str.." ("..common.durationtostring(item.duration)..")"
  193.             end
  194.             if item.nb_played > 0 then
  195.                 str = str.." [played "..tostring(item.nb_played).." time"
  196.                 if item.nb_played > 1 then
  197.                     str = str .. "s"
  198.                 end
  199.                 str = str .. "]"
  200.             end
  201.             client:append(str)
  202.         end
  203.         if item.children then
  204.             for _, c in ipairs(item.children) do
  205.                 playlist0(c,prefix.."  ")
  206.             end
  207.         end
  208.     end
  209.     local playlist
  210.     local tree = playlist_is_tree(client)
  211.     if name == "search" then
  212.         playlist = vlc.playlist.search(arg or "", tree)
  213.     else
  214.         if tonumber(arg) then
  215.             playlist = vlc.playlist.get(tonumber(arg), tree)
  216.         elseif arg then
  217.             playlist = vlc.playlist.get(arg, tree)
  218.         else
  219.             playlist = vlc.playlist.get(nil, tree)
  220.         end
  221.     end
  222.     if name == "search" then
  223.         client:append("+----[ Search - "..(arg or "`reset'").." ]")
  224.     else
  225.         client:append("+----[ Playlist - "..playlist.name.." ]")
  226.     end
  227.     if playlist.children then
  228.         for _, item in ipairs(playlist.children) do
  229.             playlist0(item)
  230.         end
  231.     else
  232.         playlist0(playlist)
  233.     end
  234.     if name == "search" then
  235.         client:append("+----[ End of search - Use `search' to reset┬á]")
  236.     else
  237.         client:append("+----[ End of playlist ]")
  238.     end
  239. end
  240.  
  241. function playlist_sort(name,client,arg)
  242.     if not arg then
  243.         client:append("Valid sort keys are: id, title, artist, genre, random, duration, album.")
  244.     else
  245.         local tree = playlist_is_tree(client)
  246.         vlc.playlist.sort(arg,false,tree)
  247.     end
  248. end
  249.  
  250. function services_discovery(name,client,arg)
  251.     if arg then
  252.         if vlc.sd.is_loaded(arg) then
  253.             vlc.sd.remove(arg)
  254.             client:append(arg.." disabled.")
  255.         else
  256.             vlc.sd.add(arg)
  257.             client:append(arg.." enabled.")
  258.         end
  259.     else
  260.         local sd = vlc.sd.get_services_names()
  261.         client:append("+----[ Services discovery ]")
  262.         for n,ln in pairs(sd) do
  263.             local status
  264.             if vlc.sd.is_loaded(n) then
  265.                 status = "enabled"
  266.             else
  267.                 status = "disabled"
  268.             end
  269.             client:append("| "..n..": " .. ln .. " (" .. status .. ")")
  270.         end
  271.         client:append("+----[ End of services discovery ]")
  272.     end
  273. end
  274.  
  275. function print_text(label,text)
  276.     return function(name,client)
  277.         client:append("+----[ "..label.." ]")
  278.         client:append "|"
  279.         for line in string.gmatch(text,".-\r?\n") do
  280.             client:append("| "..string.gsub(line,"\r?\n",""))
  281.         end
  282.         client:append "|"
  283.         client:append("+----[ End of "..string.lower(label).." ]")
  284.     end
  285. end
  286.  
  287. function help(name,client,arg)
  288.     local width = client.env.width
  289.     local long = (name == "longhelp")
  290.     local extra = ""
  291.     if arg then extra = "matching `" .. arg .. "' " end
  292.     client:append("+----[ Remote control commands "..extra.."]")
  293.     for i, cmd in ipairs(commands_ordered) do
  294.         if (cmd == "" or not commands[cmd].adv or long)
  295.         and (not arg or string.match(cmd,arg)) then
  296.             local str = "| " .. cmd
  297.             if cmd ~= "" then
  298.                 local val = commands[cmd]
  299.                 if val.aliases then
  300.                     for _,a in ipairs(val.aliases) do
  301.                         str = str .. ", " .. a
  302.                     end
  303.                 end
  304.                 if val.args then str = str .. " " .. val.args end
  305.                 if #str%2 == 1 then str = str .. " " end
  306.                 str = str .. string.rep(" .",(width-(#str+#val.help)-1)/2)
  307.                 str = str .. string.rep(" ",width-#str-#val.help) .. val.help
  308.             end
  309.             client:append(str)
  310.         end
  311.     end
  312.     client:append("+----[ end of help ]")
  313. end
  314.  
  315. function input_info(name,client)
  316.     local item = vlc.input.item()
  317.     if(item == nil) then return end
  318.     local categories = item:info()
  319.     for cat, infos in pairs(categories) do
  320.         client:append("+----[ "..cat.." ]")
  321.         client:append("|")
  322.         for name, value in pairs(infos) do
  323.             client:append("| "..name..": "..value)
  324.         end
  325.         client:append("|")
  326.     end
  327.     client:append("+----[ end of stream info ]")
  328. end
  329.  
  330. function stats(name,client)
  331.     local item = vlc.input.item()
  332.     if(item == nil) then return end
  333.     local stats_tab = item:stats()
  334.  
  335.     client:append("+----[ begin of statistical info")
  336.     client:append("+-[Incoming]")
  337.     client:append("| input bytes read : "..string.format("%8.0f KiB",stats_tab["read_bytes"]/1024))
  338.     client:append("| input bitrate    :   "..string.format("%6.0f kb/s",stats_tab["input_bitrate"]*8000))
  339.     client:append("| demux bytes read : "..string.format("%8.0f KiB",stats_tab["demux_read_bytes"]/1024))
  340.     client:append("| demux bitrate    :   "..string.format("%6.0f kb/s",stats_tab["demux_bitrate"]*8000))
  341.     client:append("| demux corrupted  :    "..string.format("%5i",stats_tab["demux_corrupted"]))
  342.     client:append("| discontinuities  :    "..string.format("%5i",stats_tab["demux_discontinuity"]))
  343.     client:append("|")
  344.     client:append("+-[Video Decoding]")
  345.     client:append("| video decoded    :    "..string.format("%5i",stats_tab["decoded_video"]))
  346.     client:append("| frames displayed :    "..string.format("%5i",stats_tab["displayed_pictures"]))
  347.     client:append("| frames lost      :    "..string.format("%5i",stats_tab["lost_pictures"]))
  348.     client:append("|")
  349.     client:append("+-[Audio Decoding]")
  350.     client:append("| audio decoded    :    "..string.format("%5i",stats_tab["decoded_audio"]))
  351.     client:append("| buffers played   :    "..string.format("%5i",stats_tab["played_abuffers"]))
  352.     client:append("| buffers lost     :    "..string.format("%5i",stats_tab["lost_abuffers"]))
  353.     client:append("|")
  354.     client:append("+-[Streaming]")
  355.     client:append("| packets sent     :    "..string.format("%5i",stats_tab["sent_packets"]))
  356.     client:append("| bytes sent       : "..string.format("%8.0f KiB",stats_tab["sent_bytes"]/1024))
  357.     client:append("| sending bitrate  :   "..string.format("%6.0f kb/s",stats_tab["send_bitrate"]*8000))
  358.     client:append("+----[ end of statistical info ]")
  359. end
  360.  
  361. function playlist_status(name,client)
  362.     local item = vlc.input.item()
  363.     if(item ~= nil) then
  364.         client:append( "( new input: " .. vlc.strings.decode_uri(item:uri()) .. " )" )
  365.     end
  366.     client:append( "( audio volume: " .. tostring(vlc.volume.get()) .. " )")
  367.     client:append( "( state " .. vlc.playlist.status() .. " )")
  368. end
  369.  
  370. function is_playing(name,client)
  371.     if vlc.input.is_playing() then client:append "1" else client:append "0" end
  372. end
  373.  
  374. function get_title(name,client)
  375.     local item = vlc.input.item()
  376.     if item then
  377.         client:append(item:name())
  378.     else
  379.         client:append("")
  380.     end
  381. end
  382.  
  383. function ret_print(foo,start,stop)
  384.     local start = start or ""
  385.     local stop = stop or ""
  386.     return function(discard,client,...) client:append(start..tostring(foo(...))..stop) end
  387. end
  388.  
  389. function get_time(var)
  390.     return function(name,client)
  391.         local input = vlc.object.input()
  392.         client:append(math.floor(vlc.var.get( input, var )))
  393.     end
  394. end
  395.  
  396. function titlechap(name,client,value)
  397.     local input = vlc.object.input()
  398.     local var = string.gsub( name, "_.*$", "" )
  399.     if value then
  400.         vlc.var.set( input, var, value )
  401.     else
  402.         local item = vlc.var.get( input, var )
  403.         -- Todo: add item name conversion
  404.         client:append(item)
  405.     end
  406. end
  407.  
  408. function titlechap_offset(var,offset)
  409.     local input = vlc.object.input()
  410.     vlc.var.set( input, var, vlc.var.get( input, var ) + offset )
  411. end
  412.  
  413. function title_next(name,client,value)
  414.     titlechap_offset('title', 1)
  415. end
  416.  
  417. function title_previous(name,client,value)
  418.     titlechap_offset('title', -1)
  419. end
  420.  
  421. function chapter_next(name,client,value)
  422.     titlechap_offset('chapter', 1)
  423. end
  424.  
  425. function chapter_previous(name,client,value)
  426.     titlechap_offset('chapter', -1)
  427. end
  428.  
  429. function seek(name,client,value)
  430.     common.seek(value)
  431. end
  432.  
  433. function volume(name,client,value)
  434.     if value then
  435.         common.volume(value)
  436.     else
  437.         client:append(tostring(vlc.volume.get()))
  438.     end
  439. end
  440.  
  441. function rate(name,client,value)
  442.     local input = vlc.object.input()
  443.     if name == "rate" then
  444.         vlc.var.set(input, "rate", tonumber(value))
  445.     elseif name == "normal" then
  446.         vlc.var.set(input,"rate",1)
  447.     else
  448.         vlc.var.set(input,"rate-"..name,nil)
  449.     end
  450. end
  451.  
  452. function frame(name,client)
  453.     vlc.var.trigger_callback(vlc.object.input(),"frame-next");
  454. end
  455.  
  456. function listvalue(obj,var)
  457.     return function(client,value)
  458.         local o = vlc.object.find(nil,obj,"anywhere")
  459.         if not o then return end
  460.         if value then
  461.             vlc.var.set( o, var, value )
  462.         else
  463.             local c = vlc.var.get( o, var )
  464.             local v, l = vlc.var.get_list( o, var )
  465.             client:append("+----[ "..var.." ]")
  466.             for i,val in ipairs(v) do
  467.                 local mark = (val==c)and " *" or ""
  468.                 client:append("| "..tostring(val).." - "..tostring(l[i])..mark)
  469.             end
  470.             client:append("+----[ end of "..var.." ]")
  471.         end
  472.     end
  473. end
  474.  
  475. function menu(name,client,value)
  476.     local map = { on='show', off='hide', up='up', down='down', left='prev', right='next', ['select']='activate' }
  477.     if map[value] and vlc.osd.menu[map[value]] then
  478.         vlc.osd.menu[map[value]]()
  479.     else
  480.         client:append("Unknown menu command '"..tostring(value).."'")
  481.     end
  482. end
  483.  
  484. function hotkey(name, client, value)
  485.     if not value then
  486.         client:append("Please specify a hotkey (ie key-quit or quit)")
  487.     elseif not common.hotkey(value) and not common.hotkey("key-"..value) then
  488.         client:append("Unknown hotkey '"..value.."'")
  489.     end
  490. end
  491.  
  492. function eval(client,val)
  493.     client:append(tostring(loadstring("return "..val)()))
  494. end
  495.  
  496. --[[┬áDeclare commands, register their callback functions and provide
  497.      help strings here.
  498.      Syntax is:
  499.      "<command name>"; { func = <function>; [ args = "<str>"; ] help = "<str>"; [ adv = <bool>; ] [ aliases = { ["<str>";]* }; ] }
  500.      ]]
  501. commands_ordered = {
  502.     { "add"; { func = add; args = "XYZ"; help = "add XYZ to playlist" } };
  503.     { "enqueue"; { func = add; args = "XYZ"; help = "queue XYZ to playlist" } };
  504.     { "playlist"; { func = playlist; help = "show items currently in playlist" } };
  505.     { "search"; { func = playlist; args = "[string]"; help = "search for items in playlist (or reset search)" } };
  506.     { "sort"; { func = playlist_sort; args = "key"; help = "sort the playlist" } };
  507.     { "sd"; { func = services_discovery; args = "[sd]"; help = "show services discovery or toggle" } };
  508.     { "play"; { func = skip2(vlc.playlist.play); help = "play stream" } };
  509.     { "stop"; { func = skip2(vlc.playlist.stop); help = "stop stream" } };
  510.     { "next"; { func = skip2(vlc.playlist.next); help = "next playlist item" } };
  511.     { "prev"; { func = skip2(vlc.playlist.prev); help = "previous playlist item" } };
  512.     { "goto"; { func = skip2(vlc.playlist.goto); help = "goto item at index" } };
  513.     { "repeat"; { func = skip2(vlc.playlist.repeat_); args = "[on|off]"; help = "toggle playlist repeat" } };
  514.     { "loop"; { func = skip2(vlc.playlist.loop); args = "[on|off]"; help = "toggle playlist loop" } };
  515.     { "random"; { func = skip2(vlc.playlist.random); args = "[on|off]"; help = "toggle playlist random" } };
  516.     { "clear"; { func = skip2(vlc.playlist.clear); help = "clear the playlist" } };
  517.     { "status"; { func = playlist_status; help = "current playlist status" } };
  518.     { "title"; { func = titlechap; args = "[X]"; help = "set/get title in current item" } };
  519.     { "title_n"; { func = title_next; help = "next title in current item" } };
  520.     { "title_p"; { func = title_previous; help = "previous title in current item" } };
  521.     { "chapter"; { func = titlechap; args = "[X]"; help = "set/get chapter in current item" } };
  522.     { "chapter_n"; { func = chapter_next; help = "next chapter in current item" } };
  523.     { "chapter_p"; { func = chapter_previous; help = "previous chapter in current item" } };
  524.     { "" };
  525.     { "seek"; { func = seek; args = "X"; help = "seek in seconds, for instance `seek 12'" } };
  526.     { "pause"; { func = skip2(vlc.playlist.pause); help = "toggle pause" } };
  527.     { "fastforward"; { func = setarg(common.hotkey,"key-jump+extrashort"); help = "set to maximum rate" } };
  528.     { "rewind"; { func = setarg(common.hotkey,"key-jump-extrashort"); help = "set to minimum rate" } };
  529.     { "faster"; { func = rate; help = "faster playing of stream" } };
  530.     { "slower"; { func = rate; help = "slower playing of stream" } };
  531.     { "normal"; { func = rate; help = "normal playing of stream" } };
  532.     { "rate"; { func = rate; args = "[playback rate]"; help = "set playback rate to value" } };
  533.     { "frame"; { func = frame; help = "play frame by frame" } };
  534.     { "fullscreen"; { func = skip2(vlc.video.fullscreen); args = "[on|off]"; help = "toggle fullscreen"; aliases = { "f", "F" } } };
  535.     { "info"; { func = input_info; help = "information about the current stream" } };
  536.     { "stats"; { func = stats; help = "show statistical information" } };
  537.     { "get_time"; { func = get_time("time"); help = "seconds elapsed since stream's beginning" } };
  538.     { "is_playing"; { func = is_playing; help = "1 if a stream plays, 0 otherwise" } };
  539.     { "get_title"; { func = get_title; help = "the title of the current stream" } };
  540.     { "get_length"; { func = get_time("length"); help = "the length of the current stream" } };
  541.     { "" };
  542.     { "volume"; { func = volume; args = "[X]"; help = "set/get audio volume" } };
  543.     { "volup"; { func = ret_print(vlc.volume.up,"( audio volume: "," )"); args = "[X]"; help = "raise audio volume X steps" } };
  544.     { "voldown"; { func = ret_print(vlc.volume.down,"( audio volume: "," )"); args = "[X]"; help = "lower audio volume X steps" } };
  545.     { "adev"; { func = skip(listvalue("aout","audio-device")); args = "[X]"; help = "set/get audio device" } };
  546.     { "achan"; { func = skip(listvalue("aout","audio-channels")); args = "[X]"; help = "set/get audio channels" } };
  547.     { "atrack"; { func = skip(listvalue("input","audio-es")); args = "[X]"; help = "set/get audio track" } };
  548.     { "vtrack"; { func = skip(listvalue("input","video-es")); args = "[X]"; help = "set/get video track" } };
  549.     { "vratio"; { func = skip(listvalue("vout","aspect-ratio")); args = "[X]"; help = "set/get video aspect ratio" } };
  550.     { "vcrop"; { func = skip(listvalue("vout","crop")); args = "[X]"; help = "set/get video crop"; aliases = { "crop" } } };
  551.     { "vzoom"; { func = skip(listvalue("vout","zoom")); args = "[X]"; help = "set/get video zoom"; aliases = { "zoom" } } };
  552.     { "snapshot"; { func = common.snapshot; help = "take video snapshot" } };
  553.     { "strack"; { func = skip(listvalue("input","spu-es")); args = "[X]"; help = "set/get subtitles track" } };
  554.     { "hotkey"; { func = hotkey; args = "[hotkey name]"; help = "simulate hotkey press"; adv = true; aliases = { "key" } } };
  555.     { "menu"; { func = menu; args = "[on|off|up|down|left|right|select]"; help = "use menu"; adv = true } };
  556.     { "" };
  557.     { "set"; { func = set_env; args = "[var [value]]"; help = "set/get env var"; adv = true } };
  558.     { "save_env"; { func = save_env; help = "save env vars (for future clients)"; adv = true } };
  559.     { "alias"; { func = skip(alias); args = "[cmd]"; help = "set/get command aliases"; adv = true } };
  560.     { "description"; { func = print_text("Description",description); help = "describe this module" } };
  561.     { "license"; { func = print_text("License message",vlc.misc.license()); help = "print VLC's license message"; adv = true } };
  562.     { "help"; { func = help; args = "[pattern]"; help = "a help message"; aliases = { "?" } } };
  563.     { "longhelp"; { func = help; args = "[pattern]"; help = "a longer help message" } };
  564.     { "logout"; { func = logout; help = "exit (if in a socket connection)" } };
  565.     { "quit"; { func = quit; help = "quit VLC (or logout if in a socket connection)" } };
  566.     { "shutdown"; { func = shutdown; help = "shutdown VLC" } };
  567.     }
  568.  
  569. if config.eval then
  570.     commands_ordered[#commands_ordered] = { "eval"; { func = skip(eval); help = "eval some lua (*debug*)"; adv =true } }
  571. end
  572.  
  573. commands = {}
  574. for i, cmd in ipairs( commands_ordered ) do
  575.     if #cmd == 2 then
  576.         commands[cmd[1]]=cmd[2]
  577.         if cmd[2].aliases then
  578.             for _,a in ipairs(cmd[2].aliases) do
  579.                 commands[a]=cmd[1]
  580.             end
  581.         end
  582.     end
  583.     commands_ordered[i]=cmd[1]
  584. end
  585. --[[ From now on commands_ordered is a list of the different command names
  586.      and commands is a associative array indexed by the command name. ]]
  587.  
  588. -- Compute the column width used when printing a the autocompletion list
  589. env.colwidth = 0
  590. for c,_ in pairs(commands) do
  591.     if #c > env.colwidth then env.colwidth = #c end
  592. end
  593. env.coldwidth = env.colwidth + 1
  594.  
  595. -- Count unimplemented functions
  596. do
  597.     local count = 0
  598.     local list = "("
  599.     for c,v in pairs(commands) do
  600.         if v.func == fixme then
  601.             count = count + 1
  602.             if count ~= 1 then
  603.                 list = list..","
  604.             end
  605.             list = list..c
  606.         end
  607.     end
  608.     list = list..")"
  609.     if count ~= 0 and env.welcome and env.welcome ~= "" then
  610.         env.welcome = env.welcome .. "\r\nWarning: "..count.." functions are still unimplemented "..list.."."
  611.     end
  612. end
  613.  
  614. --[[ Utils ]]
  615. function split_input(input)
  616.     local input = strip(input)
  617.     local s = string.find(input," ")
  618.     if s then
  619.         return string.sub(input,0,s-1), strip(string.sub(input,s))
  620.     else
  621.         return input
  622.     end
  623. end
  624.  
  625. function call_command(cmd,client,arg)
  626.     if type(commands[cmd]) == type("") then
  627.         cmd = commands[cmd]
  628.     end
  629.     local ok, msg
  630.     if arg ~= nil then
  631.         ok, msg = pcall( commands[cmd].func, cmd, client, arg )
  632.     else
  633.         ok, msg = pcall( commands[cmd].func, cmd, client )
  634.     end
  635.     if not ok then
  636.         local a = arg or ""
  637.         if a ~= "" then a = " " .. a end
  638.         client:append("Error in `"..cmd..a.."' ".. msg)
  639.     end
  640. end
  641.  
  642. function call_libvlc_command(cmd,client,arg)
  643.     local ok, vlcerr, vlcmsg = pcall( vlc.var.libvlc_command, cmd, arg )
  644.     if not ok then
  645.         local a = arg or ""
  646.         if a ~= "" then a = " " .. a end
  647.         client:append("Error in `"..cmd..a.."' ".. vlcerr) -- when pcall fails, the 2nd arg is the error message.
  648.     end
  649.     return vlcerr
  650. end
  651.  
  652. function call_object_command(cmd,client,arg)
  653.     local var, val = split_input(arg)
  654.     local ok, vlcmsg, vlcerr, vlcerrmsg = pcall( vlc.var.command, cmd, var, val )
  655.     if not ok then
  656.         client:append("Error in `"..cmd.." "..var.." "..val.."' ".. vlcmsg) -- when pcall fails the 2nd arg is the error message
  657.     end
  658.     if vlcmsg ~= "" then
  659.         client:append(vlcmsg)
  660.     end
  661.     return vlcerr
  662. end
  663.  
  664. --[[┬áSetup host ]]
  665. require("host")
  666. h = host.host()
  667. -- No auth
  668. h.status_callbacks[host.status.password] = function(client)
  669.     client.env = common.table_copy( env )
  670.     if client.env.welcome ~= "" then
  671.         client:send( client.env.welcome .. "\r\n")
  672.     end
  673.     client:switch_status(host.status.read)
  674. end
  675. -- Print prompt when switching a client's status to `read'
  676. h.status_callbacks[host.status.read] = function(client)
  677.     client:send( client.env.prompt )
  678. end
  679.  
  680. h:listen( config.hosts or config.host or "*console" )
  681.  
  682. --[[ The main loop ]]
  683. while not vlc.misc.should_die() do
  684.     local write, read = h:accept_and_select()
  685.  
  686.     for _, client in pairs(write) do
  687.         local len = client:send()
  688.         client.buffer = string.sub(client.buffer,len+1)
  689.         if client.buffer == "" then client:switch_status(host.status.read) end
  690.     end
  691.  
  692.     for _, client in pairs(read) do
  693.         local input = client:recv(1000)
  694.         local done = false
  695.         if string.match(input,"\n$") then
  696.             client.buffer = string.gsub(client.buffer..input,"\r?\n$","")
  697.             done = true
  698.         elseif client.buffer == ""
  699.            and ((client.type == host.client_type.stdio and input == "")
  700.            or  (client.type == host.client_type.net and input == "\004")) then
  701.             -- Caught a ^D
  702.             client.buffer = "quit"
  703.             done = true
  704.         else
  705.             client.buffer = client.buffer .. input
  706.         end
  707.         if done then
  708.             local cmd,arg = split_input(client.buffer)
  709.             client.buffer = ""
  710.             client:switch_status(host.status.write)
  711.             if commands[cmd] then
  712.                 call_command(cmd,client,arg)
  713.             elseif string.sub(cmd,0,1)=='@'
  714.             and call_object_command(string.sub(cmd,2,#cmd),client,arg) == 0 then
  715.                 --
  716.             elseif client.type == host.client_type.stdio
  717.             and call_libvlc_command(cmd,client,arg) == 0 then
  718.                 --
  719.             else
  720.                 local choices = {}
  721.                 if client.env.autocompletion ~= 0 then
  722.                     for v,_ in common.pairs_sorted(commands) do
  723.                         if string.sub(v,0,#cmd)==cmd then
  724.                             table.insert(choices, v)
  725.                         end
  726.                     end
  727.                 end
  728.                 if #choices == 1 and client.env.autoalias ~= 0 then
  729.                     -- client:append("Aliasing to \""..choices[1].."\".")
  730.                     cmd = choices[1]
  731.                     call_command(cmd,client,arg)
  732.                 else
  733.                     client:append("Unknown command `"..cmd.."'. Type `help' for help.")
  734.                     if #choices ~= 0 then
  735.                         client:append("Possible choices are:")
  736.                         local cols = math.floor(client.env.width/(client.env.colwidth+1))
  737.                         local fmt = "%-"..client.env.colwidth.."s"
  738.                         for i = 1, #choices do
  739.                             choices[i] = string.format(fmt,choices[i])
  740.                         end
  741.                         for i = 1, #choices, cols do
  742.                             local j = i + cols - 1
  743.                             if j > #choices then j = #choices end
  744.                             client:append("  "..table.concat(choices," ",i,j))
  745.                         end
  746.                     end
  747.                 end
  748.             end
  749.         end
  750.     end
  751. end
  752.